package main

import (
    "encoding/json"
    "fmt"
    "path"
    "time"
)

type Item struct {
    Path              string              `json:"path,omitempty"`
    Offset            uint64              `json:"offset,omitempty"`
    Instances         map[string]Instance `json:"Items,omitempty"`
    CurrentVersion    string              `json:"current_version,omitempty"`
    VersionUpdateTime int64               `json:"version_update_time,omitempty"`
    ThumbInfo         string              `json:"thumb_info,omitempty"`
    Hashcode          uint64              `json:"hashcode,omitempty"`
}

func NewEmptyItem(p string) *Item {
    return &Item{
        Path:   p,
        Offset: 0,
    }
}

func (is *Item) FullPath(ns string) string {
    return path.Join(ns, is.Path)
}

func (is *Item) Rehash(o uint64) {
    is.Hashcode = 31*is.Hashcode + o
}

func (is *Item) Value() ([]byte, error) {
    return json.Marshal(is)
}

func (is *Item) Key(ns string) []byte {
    return []byte(is.FullPath(ns))
}
func (is *Item) HasVersion(version string) bool {
    _, ok := is.Instances[version]
    return ok
}

func (is *Item) Set(version string, prop map[string]string) error {
    if version == "" {
        version = DefaultVersion
    }
    i, ok := is.Instances[version]
    if !ok {
        return fmt.Errorf("version:%s not exsit", version)
    }
    is.Instances[version] = i.Set(prop)
    return nil
}

func (is *Item) Unset(version string, prop map[string]string) error {
    if version == "" {
        version = DefaultVersion
    }
    i, ok := is.Instances[version]
    if !ok {
        return fmt.Errorf("version:%s not exsit", version)
    }
    is.Instances[version] = i.UnSet(prop)
    return nil
}

func (is *Item) Reset(version string) error {
    if version == "" {
        version = DefaultVersion
    }
    i, ok := is.Instances[version]
    if !ok {
        return fmt.Errorf("version:%s not exsit", version)
    }
    is.Instances[version] = i.Reset()
    return nil
}

func (is *Item) IsEmpty() bool {
    return is.ThumbInfo == ""
}

func (is *Item) Initialize() {
    is.Instances = make(map[string]Instance)
    is.Instances[DefaultVersion] = EmptyInstance()
    is.CurrentVersion = DefaultVersion
    is.VersionUpdateTime = time.Now().UnixNano()
    is.ThumbInfo = fmt.Sprintf("%s,%v", is.Path, is.VersionUpdateTime)
}

func (is *Item) Delete() {
    is.ThumbInfo = ""
}

func (is *Item) ChangeVersionTo(version string) error {
    if version == "" {
        version = DefaultVersion
    }
    if !is.Contains(version) {
        return fmt.Errorf("version %s not exsit", version)
    }
    if version != is.CurrentVersion {
        is.CurrentVersion = version
        is.VersionUpdateTime = time.Now().UnixNano()
    }
    return nil
}
func (is *Item) CreateVersion(version string) error {
    if version == "" {
        version = DefaultVersion
    }
    if is.Contains(version) {
        return fmt.Errorf("version %s exsits", version)
    }
    is.Instances[version] = EmptyInstance()
    return nil
}
func (is *Item) DeleteVersion(version string) error {
    if version == "" {
        version = DefaultVersion
    }
    if !is.Contains(version) {
        return fmt.Errorf("version %s not exsit", version)
    }
    if version == is.CurrentVersion {
        return fmt.Errorf("can't delete current version %s", version)
    }
    delete(is.Instances, version)
    return nil
}

func (is *Item) SaveCurrentAsVersion(version string) error {
    i, ok := is.Instances[version]
    if !ok {
        return fmt.Errorf("version %s exsit", version)
    }
    ii := i.Clone()
    is.Instances[version] = ii
    return nil
}

func (is *Item) Contains(v string) bool {
    _, ok := is.Instances[v]
    return ok
}

type Instance struct {
    Properties map[string]string `json:"properties,omitempty"`
    UpdateTime time.Time         `json:"update_time,omitempty"`
}

func (i Instance) Set(prop map[string]string) Instance {
    p := make(map[string]string)
    for k, v := range prop {
        p[k] = v
    }
    return Instance{
        Properties: p,
        UpdateTime: time.Now(),
    }
}
func (i Instance) UnSet(prop map[string]string) Instance {
    p := make(map[string]string)
    for k, v := range i.Properties {
        _, ok := prop[k]
        if !ok {
            p[k] = v
        }
    }
    return Instance{
        Properties: p,
        UpdateTime: time.Now(),
    }
}

func EmptyInstance() Instance {
    return Instance{
        Properties: make(map[string]string),
        UpdateTime: time.Now(),
    }
}

func (i Instance) Reset() Instance {
    return EmptyInstance()
}
func (i Instance) Clone() Instance {
    props := make(map[string]string)
    for k, v := range i.Properties {
        props[k] = v
    }
    return Instance{
        Properties: props,
        UpdateTime: time.Now(),
    }
}

const DefaultVersion = "default"

/*
    default action,handle offset
    action ok
 */
